﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RDotNet;
using MyProject01.Controller;

namespace MyProject01.RProjects
{
    enum UnitFunctions
    {
        LinearUnit,
        SigmoidUnit,
        TanhUnit,
        RectifiedLinearUnit,
        SoftplusUnit,
        SoftmaxUnit,
        MaxoutUnit
    };

    enum FineTuneFunctions
    {
        Backpropagation,
        Rpropagation,
        Minimizeclassifier,
        Minimizeautoencoder
    };

    class RDataObject
    {
        private REngine _re;
        private SymbolicExpression _dataOject;

        public SymbolicExpression Data { get { return _dataOject; } }

        public RDataObject(REngine re, SymbolicExpression dataObject)
        {
            _re = re;
            _dataOject = dataObject;
        }

    }
    class DarchObject
    {
        public int BatchSize;
        public int NumEpochs;
        public double StopClassError;
        public int InputLength;
        public int OutputLength;
        public int[] HidenNum;
        public double Dropout;
        public int RbmNumEpochs;
        public int RbmBatchSize;
        public int RbmLastLayer;
        public double RbmLearnRate;

        private SymbolicExpression _darchObject;

        private Function _trainingFunc;
        private Function _runFun;
        private REngineWrapper _re;

        public DarchObject()
        {
            this.BatchSize = 5;
            this.NumEpochs = 10;
            this.StopClassError = 0.01;
            this.InputLength = 1;
            this.OutputLength = 1;
            this.HidenNum = new int[] { 5, 5, 5 };
            this.Dropout = 0;

            this.RbmNumEpochs = 0;
            this.RbmBatchSize = 1;
            // this.RbmLastLayer = this.HidenNum.Length-1;
            this.RbmLastLayer = 1;
            this.RbmLearnRate = 1;


        }

        public void Init(DataBlock[] inDataBlock, DataBlock[] sigDataBlock)
        {
            _re = new REngineWrapper();
            _re.Source("DarchAPI.R");

            NumericMatrix inData = ConvertMatrix(inDataBlock);
            NumericMatrix sigData = ConvertMatrix(sigDataBlock);

            this.HidenNum = new int[]
            {
                inData.ColumnCount,
                inData.ColumnCount * 2,
                sigData.ColumnCount
            };

            var createFunc = _re.Execute("DarchCreateObject").AsFunction();
            var parmList = new SymbolicExpression[]
            {
                inData,
                sigData,
                _re.Engine.CreateIntegerVector(this.HidenNum),
                _re.Engine.CreateInteger(this.BatchSize),
                _re.Engine.CreateInteger(this.NumEpochs),
                _re.Engine.CreateNumeric(this.Dropout),
                _re.Engine.CreateNumeric(this.StopClassError),
                _re.Engine.CreateInteger(this.RbmNumEpochs),
                _re.Engine.CreateInteger(this.RbmBatchSize),
                _re.Engine.CreateInteger(this.RbmLastLayer),
                _re.Engine.CreateNumeric(this.RbmLearnRate),
            };


            _darchObject = createFunc.Invoke(parmList);

            _trainingFunc = _re.Execute("DarchTrain").AsFunction();
            _runFun = _re.Execute("DarchRun").AsFunction();

        }

        public void Run()
        {
            _darchObject = _trainingFunc.Invoke(_darchObject);
        }

        public RDataObject CreateData(DataBlock[] data)
        {
            NumericMatrix mat = ConvertMatrix(data);
            return new RDataObject(_re.Engine, mat);
        }
        public RDataObject CreateData(DataBlock data)
        {
            NumericVector vec = _re.Engine.CreateNumericVector(data.Length);
            for(int i=0; i<vec.Length; i++)
            {
                vec[i] = data[i];
            }
            return new RDataObject(_re.Engine, vec);
        }

        public DataBlock Calculate(DataBlock[] data)
        {
            RDataObject rData = CreateData(data);
            return Calculate(rData);
        }
        public DataBlock Calculate(DataBlock data)
        {
            DataBlock[] arr = new DataBlock[1];
            arr[0] = data;
            RDataObject rData = CreateData(arr);

            return Calculate(rData);
        }
        public DataBlock Calculate(RDataObject data)
        {
            NumericVector vec = _runFun.Invoke(_darchObject, data.Data).AsNumeric();
            DataBlock res = new DataBlock(vec.Length);
            for(int i=0;i<res.Length;i++)
            {
                res[i] = vec[i];
            }

            return res;
        }

        private NumericMatrix ConvertMatrix(DataBlock[] inData)
        {
            NumericMatrix inDataMat = new NumericMatrix(_re.Engine, inData.Length, inData[0].Length);
            for (int row = 0; row < inDataMat.RowCount; row++)
            {
                for (int col = 0; col < inDataMat.ColumnCount; col++)
                {
                    inDataMat[row, col] = inData[row][col];
                }
            }
            return inDataMat;
        }

    }

}
